package org.example.java.util.concurrency;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class NotifyAllDemo {

  private final List<String> syncList;

  public NotifyAllDemo() {
    // create a new synchronized list to be used
    syncList = Collections.synchronizedList(new LinkedList<String>());
  }

  // method used to remove an element from the list
  public String removeElement() throws InterruptedException {
    synchronized (syncList) {
      // while the list is empty, wait
      while (syncList.isEmpty()) {
        System.out.println(Thread.currentThread() + " " + "List is empty...");
        syncList.wait();//
        System.out.println(Thread.currentThread() + " " + "Waiting...");
      }
      String element = syncList.remove(0);
      System.out.println(Thread.currentThread() + " " + "remove Element:'" + element + "'");
      return element;
    }
  }

  // method to add an element in the list
  public void addElement(String element) {
    synchronized (syncList) {
      System.out.println(Thread.currentThread() + " " + "Opening...");

      // add an element and notify all that an element exists
      syncList.add(element);
      System.out.println(Thread.currentThread() + " " + "New Element:'" + element + "'");

      syncList.notify();//只会唤醒A1 A2 A3 其中一个
//      syncList.notifyAll();//唤醒所有A1 A2 A3
      System.out.println(Thread.currentThread() + " " + "notifyAll called!");
    }
    System.out.println(Thread.currentThread() + " " + "Closing...");
  }

  public static void main(String[] args) {
    final NotifyAllDemo demo = new NotifyAllDemo();

    Runnable runA = new Runnable() {
      public void run() {
        boolean interrupted = false;
        while (!interrupted && !Thread.currentThread().isInterrupted()) {
          try {
            demo.removeElement();
            Thread.sleep(3000);
          } catch (InterruptedException e) {
            System.out.println("Interrupted Exception!");
            interrupted = true;
          } catch (Exception x) {
            System.out.println("Exception thrown.");
          }
        }
      }
    };
    final Random random = new Random();
    Runnable runB = new Runnable() {
      // run adds an element in the list and starts the loop
      public void run() {
        boolean interrupted = false;
        while (!interrupted && !Thread.currentThread().isInterrupted()) {
          try {
            Thread.sleep(3000);
          } catch (InterruptedException e) {
            System.out.println("Interrupted Exception!");
            interrupted = true;
          }
          demo.addElement(String.valueOf(random.nextLong()));
        }
      }
    };

    try {
      Thread threadA1 = new Thread(runA, "A1");
      Thread threadA2 = new Thread(runA, "A2");
      Thread threadA3 = new Thread(runA, "A3");
      Thread threadB = new Thread(runB, "B");
      threadA1.start();
      threadA2.start();
      threadA3.start();
      threadB.start();


      Thread.sleep(10000);

      threadA1.interrupt();
      threadA2.interrupt();
      threadA3.interrupt();
      threadB.interrupt();
    } catch (InterruptedException ignore) {
    }
  }
}
